#include "command.h"

void InitCommandTable(void)
{
	commandTable = NewSymbolTable();
	DefineCommand("list", ListCmd);
	DefineCommand("run", RunCmd);
	DefineCommand("help", HelpCmd);
	DefineCommand("quit", QuitCmd);
	DefineCommand("let", LetCmd);
	DefineCommand("print", PrintCmd);
	DefineCommand("input", InputCmd);
	DefineCommand("rem", RemCmd);
	DefineCommand("if", IfCmd);
	DefineCommand("goto", GotoCmd);
}
void DefineCommand(string cmd, commandFnT fn)
{
	commandEntryT entry;
	entry = New(commandEntryT);
	entry->fn = fn;
	Enter(commandTable, cmd, entry);
}
void ExecuteCommand(scannerADT scanner, string savedCommands[])
{
	commandEntryT entry;
	string cmd;
	cmd = ReadToken(scanner);
	entry = Lookup(commandTable,cmd);
	if (entry == UNDEFINED) {
		printf("Undefined command: %s\n", cmd);
		return;
	}
	if (entry->fn == ListCmd)
		entry->fn(savedCommands);
	else if (entry->fn == RunCmd)
		entry->fn(scanner, savedCommands);
	else if (entry->fn == InputCmd || entry->fn == PrintCmd || entry->fn == LetCmd)
		entry->fn(scanner);
	else
		entry->fn();
}

void ListCmd(string arr[])
{
	int i;

	for(i = 0; i < MAX_SIZE; i++)
		if (arr[i] != NULL)
			printf("%3d %s\n", i, arr[i]);
}

void RunCmd(scannerADT scanner, string savedCommands[])
{
	int i;
	commandEntryT entry;
	scannerADT cmdScanner;
	string cmd;
	
	cmdScanner = NewScanner();
	SetScannerSpaceOption(cmdScanner, IgnoreSpaces);
	for(i = 0; i < MAX_SIZE; i++)
		if (savedCommands[i] != NULL)
		{
			SetScannerString(cmdScanner, savedCommands[i]);
			cmd = ReadToken(cmdScanner);
			if (StringEqual(cmd, "end"))
			{
				printf("Program terminated!\n");
				break;
			}
			entry = Lookup(commandTable,cmd);
			if (entry == UNDEFINED)
			{
				printf("Undefined command: %s\n", cmd);
				return;
			}
			if (entry->fn == InputCmd || entry->fn == PrintCmd || entry->fn == LetCmd)
				entry->fn(cmdScanner);
			else if (entry->fn == GotoCmd) 
				i = GotoCmd(cmdScanner) - 1;
			else if (entry->fn == IfCmd)
				i = IfCmd(cmdScanner, i) - 1;
		}
}

void HelpCmd(void)
{
	printf("This program implements a minimal basic interpreter\n\n");
	printf("Commands:\n");
	printf("  RUN\t\t -- Runs current program\n");
	printf("  LIST\t\t -- Lists the program\n");
	printf("  HELP\t\t -- List help message\n");
	printf("  QUIT\t\t -- Exit program\n\n");
	printf("Statements:\n");
	printf("  REM . . .\t -- Remark\n");
	printf("  LET v = exp\t -- Assignment\n");
	printf("  INPUT\t\t -- Reads value from user\n");
	printf("  PRINT exp\t -- Prints value of expression\n");
	printf("  GOTO n\t -- Goes to line n\n");
	printf("  IF c THEN n\t -- Goes to line n if c is TRUE\n\n");
}

void QuitCmd(void) // klar
{
	exit(EXIT_SUCCESS);
}

void LetCmd(scannerADT scanner)
{
	expressionADT exp;
	string var, value = "";
	int val;

	try {
		var = ReadToken(scanner);
		while (!isdigit(value[0]) && !isalpha(value[0]) && !StringEqual(value, "("))
			value = ReadToken(scanner);
		SaveToken(scanner, value);
		exp = ParseExp(scanner);
		val = EvalExp(exp);
		if (isalpha(var[0]))
			SetIdentifierValue(var, val);
		else
		{
			try
			{
				printf("Wrong variable name, must be alpha-character\n");
				except(ANY)
					printf("Error: %s\n", (string) GetExceptionValue());
			}endtry; 
		}
		except(ErrorException)
			printf("Error: %s\n", (string) GetExceptionValue());
	}endtry;
}

void PrintCmd(scannerADT scanner) // funkar
{
	int value;
	expressionADT exp;

	try {
	exp = ParseExp(scanner);
	value = EvalExp(exp);
	printf("%d\n", value);
	except(ErrorException)
			printf("Error: %s\n", (string) GetExceptionValue());
	} endtry;
}

void InputCmd(scannerADT scanner)
{
	string var;
	
	var = ReadToken(scanner);
	try
	{
		if (isalpha(var[0]))
		{
			printf(" "" ? "" ");
			SetIdentifierValue(var, GetInteger());
		}
		else
		{
			printf("Wrong variable name, must be alpha-character\n");
			except(ANY)
				printf("Error: %s\n", (string) GetExceptionValue());
		}

	}endtry;
}

int IfCmd(scannerADT scanner, int i)
{
	string token, buf = "", op, gotoNr;
	string lhs = "", rhs = "";
	expressionADT lhsExp, rhsExp;
	int lhsValue, rhsValue;

	while(MoreTokensExist(scanner))
	{
		token = ReadToken(scanner);
		if (!StringEqual(token, "then"))
		{
			if (StringEqual(token, "<") || StringEqual(token, ">") || StringEqual(token, "="))
			{
				op = token;
				token = ReadToken(scanner);
				while(!StringEqual(token, "then"))
				{
					rhs = Concat(rhs,token);
					rhs = Concat(rhs," "); 
					token = ReadToken(scanner);
					if (StringEqual(token, "then"))
						SaveToken(scanner, token);
				}
			}
			else
			{
				lhs = Concat(lhs, token);
				lhs = Concat(lhs, " ");
			}
		}
		else
			gotoNr = ReadToken(scanner);
	}
	try {
		SetScannerString(scanner, lhs);
		lhsExp = ParseExp(scanner);
		lhsValue = EvalExp(lhsExp);

		SetScannerString(scanner, rhs);
		rhsExp = ParseExp(scanner);
		rhsValue = EvalExp(rhsExp);
		except(ErrorException)
			printf("Error: %s\n", (string) GetExceptionValue());
	}endtry;

	if (StringEqual(op, "<") && lhsValue < rhsValue)
	{
		return StringToInteger(gotoNr); 
	}
	else if (StringEqual(op, ">") && lhsValue > rhsValue)
	{
		return StringToInteger(gotoNr); 
	}
	else if (StringEqual(op, "=") && lhsValue == rhsValue)
	{
		return StringToInteger(gotoNr); 
	}
	else return i+1;
}

void RemCmd(void)
{
	return;
}

int GotoCmd(scannerADT scanner)
{
	return StringToInteger(ReadToken(scanner));
}